﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml.Serialization;

namespace Look_Through_Windows.Helper
{
    public static class Helpers
    {
        public static double Lerp( double alpha, double min, double max )
        {
            return min * ( 1 - alpha ) + max * alpha;
        }

        public static Color Lerp( double alpha, Color min, Color max )
        {
            return Color.FromArgb(
                ( int )Lerp( alpha, min.A, max.A ),
                ( int )Lerp( alpha, min.R, max.R ),
                ( int )Lerp( alpha, min.G, max.G ),
                ( int )Lerp( alpha, min.B, max.B ) );
        }

        public static double LerpInv( double x, double min, double max )
        {
            return ( x - min ) / ( max - min );
        }

        public static double Saturate( double alpha, double min = 0, double max = 1 )
        {
            if ( alpha < min ) return min;
            if ( alpha > max ) return max;
            return alpha;
        }

        public static double LerpSaturate( double alpha, double min, double max )
        {
            return Lerp( Saturate( alpha ), min, max );
        }

        public static double SinSqrInterpolRev( double x, double min, double mid, double max )
        {
            if ( x < min ) return 0;
            if ( x < mid ) return Math.Pow( Math.Sin( LerpInv( x, min, mid ) * Math.PI / 2 ), 2 );
            if ( x < max ) return Math.Pow( Math.Cos( LerpInv( x, mid, max ) * Math.PI / 2 ), 2 );
            return 0;
        }

        public static Color ColorMix( List<Color> colors, List<double> weights )
        {
            if ( colors.Count != weights.Count ) throw new InvalidOperationException( );
            double r = 0, g = 0, b = 0, a = 0;
            double sum = 0;
            for ( int i = 0 ; i < colors.Count ; ++i )
            {
                Color c = colors[ i ];
                double w = weights[ i ];
                r += w * c.R;
                g += w * c.G;
                b += w * c.B;
                a += w * c.A;
                sum += w;
            }
            if ( sum == 0 ) return Color.Transparent;
            r /= sum;
            g /= sum;
            b /= sum;
            a /= sum;
            return Color.FromArgb( ( int )a, ( int )r, ( int )g, ( int )b );
        }

        public static void Serialize<T>( T obj, string file )
        {
            BinaryFormatter serializer = new BinaryFormatter( );
            FileStream textWriter = new FileStream( file, FileMode.Create );
            serializer.Serialize( textWriter, obj );
            textWriter.Close( );
        }

        public static T Deserialize<T>( string file )
        {
            BinaryFormatter deserializer = new BinaryFormatter( );
            FileStream textReader = new FileStream( file, FileMode.Open );
            T obj = ( T )deserializer.Deserialize( textReader );
            textReader.Close( );
            return obj;
        }
        public static void XMLSerialize<T>( T obj, string file )
        {
            XmlSerializer serializer = new XmlSerializer( typeof( T ) );
            FileStream textWriter = new FileStream( file, FileMode.Create );
            serializer.Serialize( textWriter, obj );
            textWriter.Close( );
        }

        public static T XMLDeserialize<T>( string file )
        {
            XmlSerializer deserializer = new XmlSerializer( typeof( T ) );
            FileStream textReader = new FileStream( file, FileMode.Open );
            T obj = ( T )deserializer.Deserialize( textReader );
            textReader.Close( );
            return obj;
        }

        [DllImport( "user32.dll" )]
        public static extern IntPtr GetForegroundWindow( );

        [DllImport( "user32.dll", SetLastError = true )]
        public static extern IntPtr FindWindow( string lpClassName, string lpWindowName );

        /// <summary>
        /// serializes an object to raw data
        /// </summary>
        /// <param name="anything">Anything.</param>
        /// <returns></returns>
        public static byte[] RawSerialize( object anything )
        {
            int rawsize =
                Marshal.SizeOf( anything );
            byte[] rawdata = new byte[ rawsize ];
            GCHandle handle =
                GCHandle.Alloc( rawdata,
                GCHandleType.Pinned );
            Marshal.StructureToPtr( anything,
                handle.AddrOfPinnedObject( ),
                false );
            handle.Free( );
            return rawdata;
        }
        /// <summary>
        /// Deserialize raw data
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        public static T RawDeserialize<T>( byte[] buffer )
        {
            GCHandle handle =
                GCHandle.Alloc( buffer,
                GCHandleType.Pinned );
            T temp = ( T )
                Marshal.PtrToStructure(
                handle.AddrOfPinnedObject( ),
                typeof( T ) );
            handle.Free( );
            return temp;
        }

        /// <summary>
        /// Gets an image with checker
        /// </summary>
        public static Bitmap GetCheckerImage( int width, int height, int checkerWidth, int checkerHeight, Color c1, Color c2 )
        {
            bool[ , ] bf = new bool[ height, width ];
            for ( int y = 0 ; y < height ; ++y )
                for ( int x = 0 ; x < width ; ++x )
                    bf[ y, x ] = ( ( x / checkerWidth ) + ( y / checkerHeight ) ) % 2 == 0;
            return bf.Render( b => b ? c1 : c2 );
        }

        /// <summary>
        /// Convert HSV to RGB
        /// h is from 0-360
        /// s,v values are 0-1
        /// r,g,b values are 0-255
        /// Based upon http://ilab.usc.edu/wiki/index.php/HSV_And_H2SV_Color_Space#HSV_Transformation_C_.2F_C.2B.2B_Code_2
        /// </summary>
        public static void HsvToRgb( double h, double S, double V, out int r, out int g, out int b )
        {
            // ######################################################################
            // T. Nathan Mundhenk
            // mundhenk@usc.edu
            // C/C++ Macro HSV to RGB

            double H = h;
            while ( H < 0 ) { H += 360; };
            while ( H >= 360 ) { H -= 360; };
            double R, G, B;
            if ( V <= 0 )
            { R = G = B = 0; }
            else if ( S <= 0 )
            {
                R = G = B = V;
            }
            else
            {
                double hf = H / 60.0;
                int i = ( int )Math.Floor( hf );
                double f = hf - i;
                double pv = V * ( 1 - S );
                double qv = V * ( 1 - S * f );
                double tv = V * ( 1 - S * ( 1 - f ) );
                switch ( i )
                {

                    // Red is the dominant color

                    case 0:
                        R = V;
                        G = tv;
                        B = pv;
                        break;

                    // Green is the dominant color

                    case 1:
                        R = qv;
                        G = V;
                        B = pv;
                        break;
                    case 2:
                        R = pv;
                        G = V;
                        B = tv;
                        break;

                    // Blue is the dominant color

                    case 3:
                        R = pv;
                        G = qv;
                        B = V;
                        break;
                    case 4:
                        R = tv;
                        G = pv;
                        B = V;
                        break;

                    // Red is the dominant color

                    case 5:
                        R = V;
                        G = pv;
                        B = qv;
                        break;

                    // Just in case we overshoot on our math by a little, we put these here. Since its a switch it won't slow us down at all to put these here.

                    case 6:
                        R = V;
                        G = tv;
                        B = pv;
                        break;
                    case -1:
                        R = V;
                        G = pv;
                        B = qv;
                        break;

                    // The color is not defined, we should throw an error.

                    default:
                        //LFATAL("i Value error in Pixel conversion, Value is %d", i);
                        R = G = B = V; // Just pretend its black/white
                        break;
                }
            }
            r = Clamp( ( int )( R * 255.0 ) );
            g = Clamp( ( int )( G * 255.0 ) );
            b = Clamp( ( int )( B * 255.0 ) );
        }

        /// <summary>
        /// Clamp a value to 0-255
        /// </summary>
        private static int Clamp( int i )
        {
            if ( i < 0 ) return 0;
            if ( i > 255 ) return 255;
            return i;
        }
    }
}